<?php
// $Id:$

/**
 * @file
 * Support file for node_import module for Ubercart products (uc_product).
 */

/**
 * Implementation of hook_node_import_fields().
 */
 
function uc_product_node_import_fields($type) {
  if (_uc_product_node_import_is_product($type)) {
		$fields = array(
          'title'         => 'Product: Name',
          'body'          => 'Product: Description',
          'model'         => 'Product: SKU',
          'list_price'    => 'Product: List price',
          'cost'          => 'Product: Cost price',
          'sell_price'    => 'Product: Sell price',
          'weight'        => 'Product: Weight',
          'weight_units'  => 'Product: Weight units',
          'length'        => 'Product: Length',
          'width'         => 'Product: Width',
          'height'        => 'Product: Height',
          'length_units'  => 'Product: Dimension units',
          'pkg_qty'       => 'Product: Package quantity',
          'default_qty'   => 'Product: Default quantity',
          'ordering'      => 'Product: List position',
          'shippable'     => 'Product: Shippable',
          'type'          => 'Product: Product type/class',
          'image'         => 'Product: Main Image',
        );
        
        if (module_exists('uc_attribute')) {
          //add attribute fields
          //get all attribute ids
          $db_result = db_query("SELECT aid FROM {uc_attributes}");
          $attribute_ids = array();
          while ($aid = db_fetch_object($db_result))
            $attribute_ids[] = $aid->aid;
          //get options for each attribute and add to fields array
          foreach ($attribute_ids as $aid) {
            $attribute = uc_attribute_load($aid);
            foreach (array('enabled', 'required', 'ordering') as $f) {
              $fields['ucatr:'. $attribute->aid. ':'. $f] =   'Product attr.: '.$attribute->name. ': '. $f;
            }
            foreach ($attribute->options as $option) {
              foreach (array('enabled', 'cost', 'price', 'weight', 'ordering') as $f) {
                $fields['ucatr:'. $attribute->aid. ':'. $option->oid. ':'. $f] =   'Product attr.: '.$attribute->name. ': '. $option->name. ': '. $f;
              }
            }
          }
        }
    return $fields;
  }
}


/**
 * Implementation of hook_node_import_prepare().
 */
function uc_product_node_import_prepare(&$node, $preview = FALSE) {
  $errors = array();
  if (_uc_product_node_import_is_product($node->type)) {
    $currency_error = t(' is not valid. It must only contain digits, an optional decimal marker (%decimal), an optional currency symbol (%currency) and optional thousands markers (%thousand).', array('%decimal' => variable_get('uc_currency_dec', '.'), '%currency' => variable_get('uc_currency_sign', '$'), '%thousand' => variable_get('uc_currency_thou', ',')));
    
    //NAME and SKU
    
    if (!isset($node->title) || $node->title == '') {
      $errors[] = t('Product name is required.');
    }
    
    if (!isset($node->model) || $node->model == '') {
      $errors[] = t('Product SKU is required.');
    }
    
    //COST / SELL / PRICE
    
    if (isset($node->cost)) {
      $cost = _uc_product_normalize_price($node->cost);
      if ($cost === FALSE) {
        $errors[] = t('Cost price \'%value\'', array('%value' => check_plain($node->cost))). $currency_error;
      }
      else {
        $node->cost = $cost;
      }
    }
    
    if (!isset($node->sell_price) || $node->sell_price == '') {
      $errors[] = t('Sell price is required');
    } else {
      $sell_price = _uc_product_normalize_price($node->sell_price);
      if ($sell_price === FALSE) {
        $errors[] = t('Sell price \'%value\'', array('%value' => check_plain($node->sell_price))). $currency_error;
      }
      else {
        $node->sell_price = $sell_price;
      }
    }
    
    if (isset($node->list_price)) {
      $list_price = _uc_product_normalize_price($node->list_price);
      if ($list_price === FALSE) {
        $errors[] = t('List price \'%value\'', array('%value' => check_plain($node->list_price))). $currency_error;
      }
      else {
        $node->list_price = $list_price;
      }
    }
    
    //WEIGHT and DIMENSIONS
    
    foreach (array('weight' => 'Weight', 'length' => 'Length', 'width' => 'Width', 'height' => 'Height') as $field => $label) {
      if (isset($node->$field)) {
        if ($node->$field == '')
          $node->$field = 0;
        else if (!is_numeric($node->$field))
          $errors[] = t('!label \'%value\' is not valid. It must be a number.', array('%value' => check_plain($node->$field), '!label' => t($label)));
        else if ($node->length < 0)
          $errors[] = t('!label \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($node->$field), '!label' => t($label)));
      }
    }
    
    if (isset($node->weight_units)) {
      $node->weight_units = strtolower($node->weight_units);
      if (!is_valid_weight_unit($node->weight_units))
      $errors[] = t('Weight unit \'%value\' is not valid. It must be one of %units.', array('%value' => check_plain($node->weight_units), '%units' => join(valid_weight_units(), ', ')));
    }
    
    if (isset($node->length_units)) {
      $node->length_units = strtolower($node->length_units);
      if (!is_valid_dimension_unit($node->length_units))
      $errors[] = t('Dimensions unit \'%value\' is not valid. It must be one of %units.', array('%value' => check_plain($node->length_units), '%units' => join(valid_dimension_units(), ', ')));
    }
    
    //PACKAGE and DEFAULT QUANTITIES
    
    foreach (array('pkg_qty' => 'Package quantity', 'default_qty' => 'Default quantity') as $field => $label) {
      if (!isset($node->$field) || $node->$field == '')
        $node->$field = 1;
      else if (!is_intval($node->$field))
        $errors[] = t('!label \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($node->$field), '!label' => t($label)));
      else if ($node->$field < 0)
        $errors[] = t('!label \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($node->$field), '!label' => t($label)));
    }
    
    //ORDERING
    
    if (!isset($node->ordering) || $node->ordering == '')
      $node->ordering = 0;
    else if (!is_intval($node->ordering))
      $errors[] = t('List position \'%value\' is not valid. It must be an integer (either positive or negative).', array('%value' => check_plain($node->ordering)));
    //don't do range check here because nothing will break if it's greater than the range allowed by the product editing form, and it allows more flexibility
    
    //SHIPPABLE
    
    if (!is_bool_value($node->shippable))
      $errors[] = t('Shippable value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($node->shippable), '%bools' => join(bool_values(), ', ')));
    else
      $node->shippable = is_true_value($node->shippable);
    
    //ATTRIBUTES
    
    if (module_exists('uc_attribute')) {
      foreach ($node as $key => $value) {
        if (stripos ($key, 'ucatr') === 0) {
          $attr = explode(':', $key);
          if (count($attr) == 3) { //attribute values
            switch ($attr[2]) {
              case 'enabled':
                if (!is_bool_value($value))
                  $errors[] = t('Attribute enabled value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                else
                  $node->$key = is_true_value($value);
                break;
              case 'required':
                if (!is_bool_value($value))
                  $errors[] = t('Attribute required value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                else
                  $node->$key = is_true_value($value);
                break;
              case 'ordering':
                if (!is_intval($value))
                  $errors[] = t('Attribute ordering value \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($value)));
                else if ($value < -10 || $value > 10)
                  $errors[] = t('Attribute ordering value \'%value\' is not valid. It must be in the range -10 to 10.', array('%value' => check_plain($value)));
                break;
            }
          } else { //attribute option values
            switch ($attr[3]) {
              case 'enabled':
                if (!is_bool_value($value))
                  $errors[] = t('Attribute option enabled value \'%value\' is not valid. It must be one of %bools.', array('%value' => check_plain($value), '%bools' => join(bool_values(), ', ')));
                else
                  $node->$key = is_true_value($value);
                break;
              
              case 'cost':
                $cost = _uc_product_normalize_price($value);
                if ($cost === FALSE)
                  $errors[] = t('Attribute option cost \'%value\'', array('%value' => check_plain($value))). $currency_error;
                else
                  $node->$key = $cost;
                break;
              
              case 'price':
                $price = _uc_product_normalize_price($value);
                if ($price === FALSE)
                  $errors[] = t('Attribute option price \'%value\'', array('%value' => check_plain($value))). $currency_error;
                else
                  $node->$key = $price;
                break;
              
              case 'weight':
                if (!is_numeric($value))
                  $errors[] = t('Attribute option weight \'%value\' is not valid. It must be a number.', array('%value' => check_plain($value)));
                else if ($value < 0)
                  $errors[] = t('Attribute option weight \'%value\' is not valid. It must be greater than or equal to 0.', array('%value' => check_plain($value)));
                break;
              
              case 'ordering':
                if (!is_intval($value))
                  $errors[] = t('Attribute option ordering value \'%value\' is not valid. It must be an integer.', array('%value' => check_plain($value)));
                else if ($value < -10 || $value > 10)
                  $errors[] = t('Attribute option ordering value \'%value\' is not valid. It must be in the range -10 to 10.', array('%value' => check_plain($value)));
                break;
            } //switch ($attr[3])
          } //if (count($attr) == 3) else
        }
      }
    }
  }
  return $errors;
}


/**
 * Implementation of hook_node_import_postprocess()
 */
function uc_product_node_import_postprocess(&$node, $preview, $error) {
  if (_uc_product_node_import_is_product($node->type) && !$preview && !$error) {
    if (module_exists('uc_attribute')) {
      //Add attribute data to database
      $attribute_enabled = array(); //$attributes[aid][field]
      $attribute_options = array(); //$attribute_options[aid][oid][field]
      foreach ($node as $key => $value) {
        if (stripos ($key, 'ucatr') === 0) {
          $attr = explode(':', $key);
          if (count($attr) == 3) { //attribute value
            $attributes[$attr[1]][$attr[2]] = $value;
          } else { //attribute option value
            $attribute_options[$attr[1]][$attr[2]][$attr[3]] = $value;
          }
        }
      }
      
      $clear_adjustments = false;
      foreach ($attribute_options as $aid => $options) {
        if (isset($attributes[$aid]['enabled']) && $attributes[$aid]['enabled']) {
          //get default values for attribute
          $attr_default = db_fetch_object(db_query("SELECT ordering, required FROM {uc_attributes} WHERE aid = %d", $aid));
          //update ordering and required fields for product
          if (isset($attributes[$aid]['ordering']))
            $attr_default->ordering = $attributes[$aid]['ordering'];
          if (isset($attributes[$aid]['required'])) 
            $attr_default->required = $attributes[$aid]['required'] ? 1 : 0;
          
          //add attribute to product if necessary
          if (db_num_rows(db_query("SELECT aid FROM {uc_product_attributes} WHERE nid = %d AND aid = %d", $node->nid, $aid)) == 0) {
            db_query("INSERT INTO {uc_product_attributes} (nid, aid, ordering, default_option, required, display) SELECT %d, aid, %d, 0, %d, display FROM {uc_attributes} WHERE aid = %d", $node->nid, $attr_default->ordering, $attr_default->required, $aid);
            //if an attribute was added we must reset the sku attribute adjustments data
            $clear_adjustments = true;
          } else { //update product attribute data
            db_query("UPDATE {uc_product_attributes} SET ordering = %d, required = %d WHERE nid = %d AND aid = %d", $attr_default->ordering, $attr_default->required, $node->nid, $aid);
          }
          
          //set options data
          foreach ($options as $oid => $fields) {
            if (isset($fields['enabled'])) {
              //remove all existing option data
              db_query("DELETE FROM {uc_product_options} WHERE nid = %d AND oid = %d", $node->nid, $oid);
              if ($fields['enabled']) {
                db_query("INSERT INTO {uc_product_options} (nid, oid, cost, price, weight, ordering) VALUES (%d, %d, %f, %f, %f, %d)",
                        $node->nid, $oid, $fields['cost'], $fields['price'], $fields['weight'], $fields['ordering']);
              } else {
                $match = 'i:'. $aid .';s:'. strlen($oid) .':"'. $oid .'";';
                db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%%%s%%'", $node->nid, $match);
              }
            }
          }
        } else { //if ($attribute_enabled[$aid])
          //remove attribute from product
          db_query("DELETE FROM {uc_product_attributes} WHERE nid = %d AND aid = %d", $node->nid, $aid);
          $clear_adjustments = true;
        }
      }
      
      if ($clear_adjustments)
        db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
    }
  }
}


/**
 * Check whether the node_import $type provided to the hooks is an ubercart product.
 */
function _uc_product_node_import_is_product($type) {
	if (in_array($type, module_invoke_all('product_types'))) {
		return TRUE;
	}
  return FALSE;
}


/**
 * Make a human formatted price (eg $123,456.78) to a plain decimal (eg 123456.78).
 */
function _uc_product_normalize_price($price) {
  $price = trim($price);
  $price = str_replace(variable_get('uc_currency_thou', ','), '', $price);
  $price = str_replace(variable_get('uc_currency_dec', '.'), '.', $price);
  $price = str_replace(variable_get('uc_currency_sign', '$'), '', $price);
  
  if (empty($price) || is_numeric($price) || preg_match('/^\d*(\.\d*)?$/', $price))
    return $price;
  return FALSE;
}


function is_intval($value) {
  return preg_match('/^[+-]?[0-9]+$/', $value) === 1;
}


function is_bool_value($val) {
  return in_array(strtolower($val), bool_values());
}
function is_true_value($val) {
  return in_array(strtolower($val), true_values());
}
function is_false_value($val) {
  return in_array(strtolower($val), false_values());
}
function bool_values() {
  return array_merge(true_values(), false_values());
}
function true_values() {
  return array('1', 'y', t('yes'), 't', t('true'));
}
function false_values() {
  return array('0', 'n', t('no'), 'f', t('false'));
}

function is_valid_weight_unit($val) {
  return in_array($val, valid_weight_units());
}
function valid_weight_units() {
  return array('lb','oz','kg','g');
}

function is_valid_dimension_unit($val) {
  return in_array($val, valid_dimension_units());
}
function valid_dimension_units() {
  return array('in','ft','cm','mm');
}